//////////////////////////////////////////////////////////////
// DevcertList
// Copyright 2008 Paul Todd
//
// This file is part of DevCertList.
// DevcertList is free software: you can redistribute it and/or modify it under 
// the terms of the GNU General Public License as published by the Free Software 
// Foundation, either version 3 of the License, or (at your option) any later version.
//
// DevcertList is distributed in the hope that it will be useful, but WITHOUT ANY 
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
// PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with DevcertList. 
// If not, see http://www.gnu.org/licenses/.
//////////////////////////////////////////////////////////////
//
//
// Version 1.0 initial release
// Version 1.2 support for binary encoded certificates
// Version 1.3 support for IMEI verification
// Version 1.4 support for UID's
// Version 1.6 updated source and binary licence
//////////////////////////////////////////////////////////////
#include "stdafx.h"

#define CAPABILITY_TCB			(0)
#define CAPABILITY_COMMDD		(1)
#define CAPABILITY_POWERMGMT	(2)
#define CAPABILITY_MULTIMEDIADD	(3)
#define CAPABILITY_READDEVICEDATA	(4)
#define CAPABILITY_WRITEDEVICEDATA	(5)
#define CAPABILITY_DRM				(6)
#define CAPABILITY_TRUSTEDUI		(7)
#define CAPABILITY_PROTSERV			(8)
#define CAPABILITY_DISKADMIN		(9)
#define CAPABILITY_NETWORKCONTROL	(10)
#define CAPABILITY_ALLFILES			(11)
#define CAPABILITY_SWEVENT			(12)
#define CAPABILITY_NETWORKSERVICES	(13)
#define CAPABILITY_TOCALSERVICES	(14)
#define CAPABILITY_READUSERDATA		(15)
#define CAPABILITY_WRITEUSERDATA	(16)
#define CAPABILITY_TOCATION			(17)
#define CAPABILITY_SURROUNDINGSDD	(18)
#define CAPABILITY_USERENVIRONMENT	(19)

BOOL IsSet(const DWORD dwBitArray, int iIndex)
	{
	const DWORD dwMap = 1 << (31 - iIndex);
	return (dwBitArray & dwMap) != 0;
	}

void LogMessage(LPCTSTR lpszMessage, ...)
	{
	TCHAR buffer[512];
	
	va_list args;
	va_start(args, lpszMessage);

	// Returns the size of the created message
	_vsntprintf(buffer, sizeof(buffer), lpszMessage, args);
	_ftprintf(stderr, buffer);
	}

void LogError(LPCTSTR lpszMessage, ...)
	{
	TCHAR buffer[512];
	
	va_list args;
	va_start(args, lpszMessage);

	// Returns the size of the created message
	_vsntprintf(buffer, sizeof(buffer), lpszMessage, args);
	_ftprintf(stderr, buffer);
	}

void RemoveAllSubstrings(LPTSTR lpszString, LPCTSTR lpszStringToRemove)
	{
	const size_t length = strlen(lpszStringToRemove);
	LPSTR pos = NULL;
	do
		{
		pos = strstr(lpszString, lpszStringToRemove);
		if (pos != NULL)
			{
			LPSTR from = pos + length;
			LPSTR to = pos;

			while (*from)      
				*to++ = *from++;  
			*to = 0;
			}

		} 
	while (pos != NULL);

	}

LPBYTE ReadCertDataFromFile(LPCTSTR lpszFilename, DWORD& cbLength)
	{
	HANDLE hFile = CreateFile(lpszFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, NULL); 

	if (hFile == INVALID_HANDLE_VALUE)
		{
		LogError(_T("STOPPING Failed to open certificate file (%d)"), GetLastError()); // failed to open file
		return NULL;
		}

	cbLength = GetFileSize(hFile, NULL);

	if (cbLength == INVALID_FILE_SIZE)
		{
		LogError(_T("STOPPING Failed to read file size (%d)"), GetLastError()); // failed to read file size
		CloseHandle(hFile);
		return NULL;
		}


	LPBYTE buf = new BYTE[cbLength];
	if (buf == NULL)
		{
		LogError(_T("STOPPING Failed to allocate memory for certificate file data")); // failed to allocate memory
		CloseHandle(hFile);
		return NULL;
		}

	DWORD cbActualRead = 0;
	if (ReadFile(hFile,buf,cbLength,&cbActualRead,NULL) == 0)
		{
		LogError(_T("STOPPING Failed to read certificate data from file (%d)"), GetLastError());
		delete buf;
		buf = NULL;

		CloseHandle(hFile);
		return NULL;
		}

	if (cbLength != cbActualRead)
		{
		LogError(_T("STOPPING Failed to read all the certificate data (%d)"), GetLastError());
		delete buf;
		buf = NULL;

		CloseHandle(hFile);
		return NULL;
		}

	CloseHandle(hFile);
	return buf;
	}

PCCERT_CONTEXT CreateContextFromFileData(LPCTSTR lpszFilename)
	{
	DWORD cbFileLength = 0;
	LPBYTE lpszFileData = ReadCertDataFromFile(lpszFilename, cbFileLength);
	if (lpszFileData == NULL)
		{
		// we  would have already logged the errors
		return NULL;
		}

	LPSTR lpszStringData = new CHAR[cbFileLength+1];
	if (lpszStringData == NULL)
		{
		LogError(_T("STOPPING Failed to allocate temporary string data"));
		delete lpszFileData;
		lpszFileData = NULL;
		return NULL;
		}

	// copy the suspected string data over and zero terminate it!
	memcpy(lpszStringData, lpszFileData, cbFileLength);
	lpszStringData[cbFileLength] = 0;

	if (strstr(lpszStringData, "-----BEGIN CERTIFICATE-----") != NULL && strstr(lpszStringData, "-----END CERTIFICATE-----") != NULL)
		{
		// this certifcate is base64 encoded

		RemoveAllSubstrings(lpszStringData, "-----BEGIN CERTIFICATE-----");
		RemoveAllSubstrings(lpszStringData, "-----END CERTIFICATE-----");

		delete [] lpszFileData;
		lpszFileData = NULL;
		cbFileLength = 0;

		cbFileLength = (DWORD)strlen(lpszStringData);
		lpszFileData = new BYTE[cbFileLength];
		if (lpszFileData == NULL)
			LogError(_T("STOPPING Failed to allocate buffer for base64 decoded data"));
		else 
			{
			int actualLength = cbFileLength;
			if (!ATL::Base64Decode(lpszStringData, cbFileLength, lpszFileData, &actualLength))
				{
				LogError(_T("STOPPING Failed to base64 decode certificate"));
				delete [] lpszFileData;
				lpszFileData = NULL;
				}
			// remember to reset the length of the data to the decoded length
			cbFileLength = actualLength;
			}

		delete [] lpszStringData;
		lpszStringData = NULL;
		}
	else
		; // this is binary encoded data so no transform  needed

	PCCERT_CONTEXT pCertContext = NULL;
	if (lpszFileData != NULL)
		{
		// Now we read the file, get the certificate from the file
		pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING, lpszFileData, cbFileLength);
		if (pCertContext == NULL)
			LogError(_T("STOPPING Failed to parse certificate data (%d)"), GetLastError());
		
		delete [] lpszFileData;
		lpszFileData = NULL;
		}

	return pCertContext;
	}

PCERT_EXTENSION FindExtension(const PCERT_INFO lpInfo, const LPSTR lpszOidId)
	{
	for (int i=0; i < (int)lpInfo->cExtension; i++)
		{
		if (strcmp(lpInfo->rgExtension[i].pszObjId,lpszOidId) == 0)
			return &(lpInfo->rgExtension[i]);
		}

	return NULL;
	}

void DumpIMEIs(const PCERT_INFO lpInfo, const BOOL bBare, const BOOL bValidate)
	{
	PCERT_EXTENSION ext = FindExtension(lpInfo, "1.2.826.0.1.1796587.1.1.1.1");
	if (ext == NULL)
		LogError(_T("STOPPING Failed to find extension field for IMEI\'s - is this a Symbian devcert?"));
	else
		{
		if (!bBare)
			LogMessage(_T("\nListing all IMEI\'s in devcert\n\n"));

		// now parse the fields
		LPBYTE lpExtData = ext->Value.pbData;
		int pos = 0;

		int i = 0;

		// skip the type of DER
		BYTE type = *lpExtData;
		if (type != 0x30)
			{
			LogError(_T("STOPPING Unexpected datatype in IMEI list structure was %x, expected 0x30"), (int)type);
			return;
			}

		pos++;

		int length = lpExtData[pos];
		pos++;

		// 0x80 means this is a custom variable data structure and is not part of our object
		if (length == 0x80)
			{
			LogError(_T("STOPPING Unxpected type in length field of IMEI blob, 0x80 is not a supported length at %d"), pos);
			return;
			}

		if (length > 0x80)
			{
			// the length is > 128 so
			// we look at the number of bytes
			// in the length and reset the length variable
			int lengthOfLength = (length & 0x7f);
			length=0;
			while (lengthOfLength > 0)
				{
				length <<= 8; // roll up the length
				length += lpExtData[pos];
				pos++;
				lengthOfLength--;
				}
			}
		else
			; // length is already set and is < 128 bytes

		int base = pos;
		BOOL warnAboutSamsungIMEIs = FALSE;
		int errors = 0;

		while (pos < (base + length))
			{
			i++;

			// we now iterate through the list until we get to the end
			const BYTE type = lpExtData[pos];
			if (type != 0x0c)
				{
				LogError(_T("STOPPING Invalid IMEI field type found in data %x expected 0x0c at %d"), (int)type, pos);
				return;
				}

			pos++;

			const BYTE length = lpExtData[pos];
			if (length >= 0x80)
				{
				LogError(_T("STOPPING Invalid length parsing IMEI %d at %d"), i, pos);
				return;
				}	
			pos++;

			const LPBYTE lpIMEI = lpExtData + pos;
#ifdef _UNICODE
			if (bBare)
				_tprintf(_T("%.*S"), length, lpIMEI);
			else
				_tprintf(_T("%-4d %.*S"), i, length, lpIMEI);
#else
			if (bBare)
				_tprintf(_T("%.*s"), length, lpIMEI);
			else
				_tprintf(_T("%-4d %.*s"), i, length, lpIMEI);
#endif

			if (!bBare && bValidate)
				{
				// Now check the IMEI for validitity
				LPCSTR lpszIMEI = (LPCSTR)lpIMEI;

				if (length < 15)
					{
					LogError(_T(" STOPPING IMEI length (%d) is less than 15"), length);
					return;
					}

				if (length != 15)
					{
					LogError(_T(" *** invalid length (found %d, expected 15) *See note below ***"), length);
					errors++;
					warnAboutSamsungIMEIs = TRUE;
					}

				int checksum = 0;
				for (int i = 0; i < 14; i++)
					{
					int v = lpszIMEI[i] - '0';
					if (v < 0 || v > 9)
						{
						LogError(_T("\nSTOPPING Invalid digit at index %d\n"), i);
						return;
						}

					if (i % 2) v *= 2;
					checksum += (v % 10) + (v / 10);
					}

				if (checksum % 10 == 0) 
					checksum = 0;
				else 
					checksum = 10 - (checksum % 10);

				CHAR targetcs = lpszIMEI[14] - '0';

				if (targetcs != checksum)
					{
					LogError(_T(" **** 3GPP IMEI checksum mismatch (%d expected %d) ***"), checksum, targetcs);
					errors++;
					}
				}
				_tprintf(_T("\n"));

			pos += length;
			}

			if (!bBare)
				{
				_tprintf(_T("\n%d IMEI\'s in the certificate\n"), i);
				if (errors != 0 && bValidate)
					{
					_tprintf(_T("------------------------------------------------------------\n"));
					_tprintf(_T("!!!!          %d CORRUPT OR SUSPICIOUS IMEI'S           !!!!\n"), errors);
					_tprintf(_T("------------------------------------------------------------\n"));
					if (warnAboutSamsungIMEIs)
						_tprintf(_T("* Some devices may have up to 17 characters for an IMEI *BUT* this is normally an error"));
					}
				}
		}
	}

void DumpUids(const PCERT_INFO lpInfo, const BOOL bBare, const BOOL bValidate)
	{
	PCERT_EXTENSION ext = FindExtension(lpInfo, "1.2.826.0.1.1796587.1.1.1.4");
	if (ext == NULL)
		LogError(_T("Failed to find extension field for UID\'s - is this an old Symbian devcert?"));
	else
		{
		if (!bBare)
			_tprintf(_T("\nListing all UID\'s in devcert\n\n"));

		
		LPBYTE lpUidData = ext->Value.pbData;
		const DWORD  dwUidDataLength = ext->Value.cbData;

		int pos = 0;

		// skip the type of DER
		BYTE type = *lpUidData;
		if (type != 0x30)
			{
			LogError(_T("STOPPING Unexpected datatype in UID list structure was %x, expected 0x30"), (int)type);
			return;
			}

		pos++;

		int length = lpUidData[pos];
		pos++;

		// 0x80 means this is a custom variable data structure and is not part of our object
		if (length == 0x80)
			{
			LogError(_T("STOPPING Unxpected type in length field of UID blob, 0x80 is not a supported length at %d"), pos);
			return;
			}

		if (length > 0x80)
			{
			// the length is > 128 so
			// we look at the number of bytes
			// in the length and reset the length variable
			int lengthOfLength = (length & 0x7f);
			length=0;
			while (lengthOfLength > 0)
				{
				length <<= 8; // roll up the length
				length += lpUidData[pos];
				pos++;
				lengthOfLength--;
				}
			}
		else
			; // length is already set and is < 128 bytes

		int base = pos;
		int errors = 0;
		int i = 0;
		while (pos < (base + length))
			{
			i++;

			// http://luca.ntop.org/Teaching/Appunti/asn1.html
			// this is a byte integer BER type - must be 2
			const BYTE type = lpUidData[pos];
			if (type != 0x02)
				{
				LogError(_T("Invalid type found for UID expected 0x02 at %d"), (int)type, pos);
				return;
				}

			pos++;

			// number of bytes 
			const BYTE size = lpUidData[pos];
			if (size != 4)
				{
				LogError(_T("Invalid uid length found expected 4 but found (%d) at %d"), (int)size, pos);
				return;
				}
			pos++;

			BYTE one = lpUidData[pos];
			pos++;
			BYTE two = lpUidData[pos];
			pos++;
			BYTE three = lpUidData[pos];
			pos++;
			BYTE four = lpUidData[pos];
			pos++;

			const DWORD uid = one << 24 | two << 16 | three << 8 | four;
			if (bBare)
				_tprintf(_T("0x%x\n"), uid);
			else
				{
				_tprintf(_T("%-4d "), i);

				if ((uid & 0x20000000) == 0x20000000)
					_tprintf(_T("0x%x - PROTECTED\n"), uid);			
				else 
					if ((uid & 0xa0000000) == 0xa0000000)
						_tprintf(_T("0x%x UNPROTECTED\n"), uid);			
					else
						{
						_tprintf(_T("0x%x **** UNKNOWN UID Type, expected UID to start with 0x2 or 0xA \n"), uid);
						errors++;
						}
				}
			}

		if (!bBare)
			{
			_tprintf(_T("\n%d UID\'s in the certificate\n"), i);
			if (errors != 0 && bValidate)
				{
				_tprintf(_T("------------------------------------------------------------\n"));
				_tprintf(_T("!!!!          %d CORRUPT OR SUSPICIOUS UID'S           !!!!\n"), errors);
				_tprintf(_T("------------------------------------------------------------\n"));
				}
			}
		}
	}

void DumpCapabilities(const PCERT_INFO lpInfo, const BOOL bBare)
	{
	PCERT_EXTENSION ext = FindExtension(lpInfo, "1.2.826.0.1.1796587.1.1.1.6");
	if (ext == NULL)
		LogError(_T("Failed to find extension field for Capabilities field - is this a Symbian devcert?"));
	else
		{
		if (!bBare)
			_tprintf(_T("\nListing all Capabilities\'s in devcert\n\n"));

		
		LPBYTE lpCapabilityData = ext->Value.pbData;

		int pos = 0;
		// http://luca.ntop.org/Teaching/Appunti/asn1.html
		// this is a byte string BER type - must be 3
		const BYTE type = lpCapabilityData[pos];
		if (type != 0x03)
			{
			LogError(_T("Invalid type found for capability list %d, exepected 0x03 at %d"), (int)type, pos);
			return;
			}

		pos++;

		// number of bytes 
		const BYTE size = lpCapabilityData[pos];
		if (size >= 0x80)
			{
			LogError(_T("Invalid length found in capabilities must be < 128 (%d) at %d"), (int)size, pos);
			return;
			}
		pos++;

		const BYTE remainingbits = lpCapabilityData[pos];
		pos++;

		// the next 4 bytes represent an array of bits
		// each 8 bits is left to right ordered but each byte is
		// right to left ordered
		BYTE one = lpCapabilityData[pos];
		pos++;
		BYTE two = lpCapabilityData[pos];
		pos++;
		BYTE three = lpCapabilityData[pos];
		pos++;
		BYTE four = lpCapabilityData[pos];
		pos++;

		// Now construct the binary string for items and parse out the
		// capabilities as a bit mask
		const DWORD dwCapabilties = one << 24 | two << 16 | three << 8 | four;

		if (IsSet(dwCapabilties, CAPABILITY_TCB))
			{
			if (bBare)
				_tprintf(_T("TCB\n"));
			else
				_tprintf(_T("   TCB\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO TCB\n"));
			}

		if (IsSet(dwCapabilties, CAPABILITY_COMMDD))
			{
			if (bBare)
				_tprintf(_T("CommDD\n"));
			else
				_tprintf(_T("   CommDD\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO CommDD\n"));
			}

		if (IsSet(dwCapabilties, CAPABILITY_POWERMGMT))
			{
			if (bBare)
				_tprintf(_T("Power Management\n"));
			else
				_tprintf(_T("   Power Management\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO Power Management\n"));
			}

		if (IsSet(dwCapabilties,CAPABILITY_MULTIMEDIADD))
			{
			if (bBare)
				_tprintf(_T("MultimediaDD\n"));
			else
				_tprintf(_T("   MultimediaDD\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO MultimediaDD\n"));
			}

		if (IsSet(dwCapabilties, CAPABILITY_READDEVICEDATA))
			{
			if (bBare)
				_tprintf(_T("Read Device Data\n"));
			else
				_tprintf(_T("   Read Device Data\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO Read Device Data\n"));
			}

		if (IsSet(dwCapabilties,CAPABILITY_WRITEDEVICEDATA))
			{
			if (bBare)
				_tprintf(_T("Write Device Data\n"));
			else
				_tprintf(_T("   Write Device Data\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO Write Device Data\n"));
			}

		if (IsSet(dwCapabilties, CAPABILITY_DRM))
			{
			if (bBare)
				_tprintf(_T("DRM\n"));
			else
				_tprintf(_T("   DRM\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO DRM\n"));
			}

		if (IsSet(dwCapabilties , CAPABILITY_TRUSTEDUI))
			{
			if (bBare)
				_tprintf(_T("Trusted UI\n"));
			else
				_tprintf(_T("   Trusted UI\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO Trusted UI\n"));
			}


		if (IsSet(dwCapabilties, CAPABILITY_PROTSERV))
			{
			if (bBare)
				_tprintf(_T("Protserv\n"));
			else
				_tprintf(_T("   Protserv\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO ProtServ\n"));
			}

		if (IsSet(dwCapabilties, CAPABILITY_DISKADMIN))
			{
			if (bBare)
				_tprintf(_T("DiskAdmin\n"));
			else
				_tprintf(_T("   DiskAdmin\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO DiskAdmin\n"));
			}

		if (IsSet(dwCapabilties, CAPABILITY_NETWORKCONTROL))
			{
			if (bBare)
				_tprintf(_T("Network Control\n"));
			else
				_tprintf(_T("   Network Control\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO Network Control\n"));
			}

		if (IsSet(dwCapabilties, CAPABILITY_ALLFILES))
			{
			if (bBare)
				_tprintf(_T("All Files\n"));
			else
				_tprintf(_T("   All Files\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO All Files\n"));
			}

		if (IsSet(dwCapabilties, CAPABILITY_SWEVENT))
			{
			if (bBare)
				_tprintf(_T("SWEvent\n"));
			else
				_tprintf(_T("   SWEvent\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO SWEvent\n"));
			}

		if (IsSet(dwCapabilties, CAPABILITY_NETWORKSERVICES))
			{
			if (bBare)
				_tprintf(_T("Network Services\n"));
			else
				_tprintf(_T("   Network Services\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO Network Services\n"));
			}

		if (IsSet(dwCapabilties, CAPABILITY_TOCALSERVICES))
			{
			if (bBare)
				_tprintf(_T("Local Services\n"));
			else
				_tprintf(_T("   Local Services\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO Local Services\n"));
			}

		if (IsSet(dwCapabilties, CAPABILITY_READUSERDATA))
			{
			if (bBare)
				_tprintf(_T("Read User Data\n"));
			else
				_tprintf(_T("   Read User Data\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO Read User Data\n"));
			}

		if (IsSet(dwCapabilties, CAPABILITY_WRITEUSERDATA))
			{
			if (bBare)
				_tprintf(_T("Write User Data\n"));
			else
				_tprintf(_T("   Write User Data\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO Write User Data\n"));
			}

		if (IsSet(dwCapabilties, CAPABILITY_TOCATION))
			{
			if (bBare)
				_tprintf(_T("Location\n"));
			else
				_tprintf(_T("   Location\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO Location\n"));
			}

		if (IsSet(dwCapabilties , CAPABILITY_SURROUNDINGSDD))
			{
			if (bBare)
				_tprintf(_T("SurroundingsDD\n"));
			else
				_tprintf(_T("   SurroundingsDD\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO SurroundingsDD\n"));
			}

		if ((dwCapabilties, CAPABILITY_USERENVIRONMENT))
			{
			if (bBare)
				_tprintf(_T("User Environment\n"));
			else
				_tprintf(_T("   User Environment\n"));
			}
		else
			{
			if (!bBare)
				_tprintf(_T("NO User Environment\n"));
			}

		}
	}


void PrintHelp()
	{
	_tprintf(_T("\nDevCertList 1.6 \n"));
	_tprintf(_T("(c) 2009 Paul Todd\n"));
	_tprintf(_T("Lists IMEI's, UID's and Capabilities for a Symbian devcerts\n\n"));
	_tprintf(_T("This software is licenced under the GPL 3.0 licence and\n"));
	_tprintf(_T("is released as freeware, you should not have been charged for it.\n"));
	_tprintf(_T("\nFor source, help, updates visit\n"));
	_tprintf(_T("https://projects.developer.nokia.com/DevCertList\n\n"));
	_tprintf(_T("usage: devcertlist [/b] [/i] [/c] [/?] certificatefile.cer\n"));
	_tprintf(_T("/b - bare output, useful for grep\n"));
	_tprintf(_T("/i - list imei's in devcert\n"));
	_tprintf(_T("/nv - do not validate IMEI's and UID's in devcert\n"));
	_tprintf(_T("/c - list capabilties for devcert\n"));
	_tprintf(_T("/u - list uids for devcert (if present)\n"));
	_tprintf(_T("/? - help\n"));
	}

BOOL FlagSet(const int argc, TCHAR* argv[], LPCTSTR lpszOption)
	{
	for (int i=0; i < argc; i++)
		{
		LPCTSTR lpszArguement = argv[i];
		if (strcmp(lpszArguement, lpszOption) == 0)
			return TRUE;
		}

	return FALSE;
	}

int _tmain(int argc, _TCHAR* argv[])
	{
		const BOOL help = FlagSet(argc, argv, _T("/?"));
		const BOOL bare = FlagSet(argc, argv, _T("/b"));
		const BOOL novalidate = FlagSet(argc, argv, _T("/nv"));
		
		const BOOL dumpIMEIs = FlagSet(argc, argv, _T("/i"));
		const BOOL dumpCapabilities = FlagSet(argc, argv, _T("/c"));
		const BOOL dumpUIDs = FlagSet(argc, argv, _T("/u"));

		if (argc <= 1 || help)
			PrintHelp();
		else
			{
			if (dumpIMEIs && dumpCapabilities && bare)
				LogError(_T("dump IMEI\'s, dump Capabilties and bare all together are not allowed"));
			else
				{
				LPCTSTR lpszCertFileName = argv[argc-1];
				PCCERT_CONTEXT pCertContext = CreateContextFromFileData(lpszCertFileName);
				if (pCertContext != NULL)
					{
					if (dumpIMEIs || (!dumpIMEIs && !dumpCapabilities && !dumpUIDs))
						DumpIMEIs(pCertContext->pCertInfo, bare, !novalidate);

					if (dumpUIDs || (!dumpUIDs && !dumpIMEIs && !dumpCapabilities))
						DumpUids(pCertContext->pCertInfo, bare, !novalidate);

					if (dumpCapabilities || (!dumpIMEIs && !dumpCapabilities && !dumpUIDs))
						DumpCapabilities(pCertContext->pCertInfo, bare);

					CertFreeCertificateContext(pCertContext);
					}
				else
					; // failed to create the context, don't log as we have already done it
				}
			}
	return 0;
	}
